Skip to content

fix(recovery): add explicit PrismaModule import to RecoveryModule - #861

Merged
Jambox11 merged 410 commits into
mux-labs:mainfrom
Xaxxoo:fix/772-add-prisma-module-to-recovery
Sep 4, 2026
Merged

fix(recovery): add explicit PrismaModule import to RecoveryModule#861
Jambox11 merged 410 commits into
mux-labs:mainfrom
Xaxxoo:fix/772-add-prisma-module-to-recovery

Conversation

@Xaxxoo

@Xaxxoo Xaxxoo commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add explicit PrismaModule import to RecoveryModule so it no longer relies on @Global() to resolve PrismaService
  • Add unit test (recovery.module.spec.ts) verifying the module compiles in isolation and that PrismaService is resolvable through the module's own import chain

Problem

RecoveryService injects PrismaService, but RecoveryModule did not explicitly import PrismaModule. It only worked because PrismaModule is decorated with @Global(). This is fragile and breaks test isolation when the global module is not loaded.

Changes

  • src/recovery/recovery.module.ts — added PrismaModule to the imports array and added the corresponding import statement
  • src/recovery/recovery.module.spec.ts — new spec that:
    • Compiles RecoveryModule in isolation (with PrismaService overridden)
    • Asserts PrismaService, RecoveryService, and AdminRecoveryService are resolvable
    • Verifies PrismaModule appears in the module's imports metadata via Reflect.getMetadata

Test plan

  • npm run test -- --testPathPattern=recovery.module.spec passes
  • Existing recovery tests remain green
  • No runtime behavior change (PrismaModule was already available via @global)

Closes #772

🤖 Generated with Claude Code

yungjay21 and others added 30 commits June 30, 2026 09:02
Add @apioperation, @ApiParam, @apiquery, @ApiBody, and @apiresponse
decorators with inline examples to the six webhook controller handlers
that previously had no Swagger coverage:

- GET /webhooks/endpoints/:id — 200 (endpoint detail) + 404
- PUT /webhooks/endpoints/:id — 200 + 400 + 404; two @ApiBody examples
  (update URL/events, disable endpoint)
- DELETE /webhooks/endpoints/:id — 204 + 404
- POST /webhooks/endpoints/:id/rotate-secret — 200 with one-time secret
  note in description + 404
- GET /webhooks/endpoints/:id/deliveries — 200 with full delivery object
  example + 404; @apiquery for page/limit
- POST /webhooks/process-deliveries — 200 with processed/delivered/
  failed/retrying summary example; admin description added

Also fixed two pre-existing issues in the file:
- Stray code sitting outside the class body (orphaned return block)
- UpdateWebhookEndpointRequest (undefined type) → UpdateWebhookEndpointDto
- Create new integration test suite (test/webhooks.integration.e2e-spec.ts) with 900+ lines of test coverage
- Tests cover 5 main areas:
  * CRUD operations: endpoint creation, listing, retrieval, updates, deletion with pagination
  * Event emission & delivery: wallet.created, transaction.confirmed, balance.updated events
  * Retry & failure handling: transient failures, consecutive failures, dead letter queue
  * Signature verification: HMAC-SHA256 signing, required webhook headers, timestamp validation
  * Secret rotation: generation, rotation, uniqueness, secure format

- Fix webhook controller imports and add missing FeatureFlag/FeatureFlagGuard imports
- Simplify webhook service to remove unused dependencies (cache, requestContext)
- Add proper error handling and validation in WebhookService

Test Statistics:
- 25+ test cases covering all major workflows
- Full endpoint coverage with realistic scenarios
- Mock axios integration for webhook delivery simulation
- Proper setup/teardown and database cleanup
- Add FeatureFlagGuard and @FeatureFlag('wallets_enabled') decorator to WalletsController
- Configure guard order: FeatureFlagGuard first, then ApiKeyGuard, then RateLimitGuard
- Feature flag check executes before authentication and rate limiting
- Allows toggling wallet API access via FEATURE_WALLETS_ENABLED environment variable

- Update WalletsController tests to override FeatureFlagGuard
- All existing tests pass (11/11 passing)

Benefits:
- Control wallet API availability without code deployment
- Graceful feature toggle during maintenance or rollout
- Consistent with webhook and other feature-controlled APIs
- Create WalletCacheService with methods for caching wallet data (142 lines)
- Implements cache management for wallet lookups by ID and user+network
- Cache key prefixes: wallet:<id> and wallet:user:<userId>:<network>
- TTL configured to 5 minutes for optimal balance of freshness and performance

Methods provided:
- getWalletById/setWalletById - Cache wallet by unique ID
- getWalletByUser/setWalletByUser - Cache wallet by user and network
- invalidateWalletById/invalidateWalletByUser - Selective cache invalidation
- invalidateUserWallets - Bulk invalidation for user across networks
- clearAllWalletCache - Full cache purge (maintenance)

- Create comprehensive unit tests (227 lines, 18 test cases)
- Tests cover cache hit/miss, multi-network scenarios, invalidation, expiration
- All tests passing (18/18)

Integration Points:
- WalletCacheService ready for injection into WalletsService
- findWalletById can leverage cache.getWalletById/setWalletById
- Cache invalidation available for wallet updates, rotations, and deletes
- Stub design allows incremental cache integration without breaking changes
- Create wallet-integration.e2e-spec.ts with 542 lines of test coverage
- Uses AppModule for full end-to-end integration testing
- Comprehensive CRUD operation tests:
  * Create wallet with idempotency support
  * List wallets with pagination and filtering
  * Get single wallet and wallet status
  * Update wallet status
  * Delete wallet
  * List wallets by user

- Multi-network wallet support tests:
  * Same user on different networks (TESTNET/MAINNET)
  * Network isolation and filtering
  * Cross-network wallet operations

- Idempotency tests:
  * Duplicate creation with same idempotency key returns cached result
  * Duplicate creation with different key returns 409 Conflict
  * isNewWallet flag correctly reflects idempotent behavior

- Pagination and filtering tests:
  * List with limit and offset
  * Max limit enforcement (100)
  * Filter by userId, network, status
  * Proper pagination metadata (hasMore, total)

- Feature flag guard tests:
  * Disabled feature handling
  * 403 Forbidden response structure

- API key authentication tests:
  * Valid/invalid key scenarios
  * Authorization enforcement

- Error handling and validation tests:
  * Invalid enum values
  * Empty required fields
  * Duplicate wallet conflict (409)
  * Not found scenarios (404)
  * Business logic validation
  * Graceful error responses

Test Statistics:
- 50+ test cases covering all major workflows
- Full endpoint coverage with realistic scenarios
- Database cleanup in afterAll hook
- Integration with real Prisma ORM and AppModule
- Tests ready for CI/CD pipeline
…che-featureflag-keymgmt-openapi

Fix/balance indexer cache featureflag keymgmt openapi
…ovements

Fix/key management improvements
…ovements

Fix/key management improvements
feat: wallet orchestrator — pagination, filtering, domain events, endpoint docs (mux-labs#415 mux-labs#416 mux-labs#417 mux-labs#419)
…rovements

Fix/key management improvements
feat: wallet orchestrator input validation, OpenAPI examples, integra…
Platform improvement for Mux Protocol
…r-metrics-env-e2e-boundaries

feat(wallets): orchestrator metrics, env validation, e2e tests, bound…
…provements

Platform improvement: Recovery API enhancements (examples, validation, pagination, filtering)
…rator-retry-backoff

feat(wallets): add retry with backoff to wallet orchestrator (mux-labs#418)
…examples

feat(webhooks): add OpenAPI examples to all webhook endpoints
…ntegration-tests

feat(webhooks): Add comprehensive integration tests
…-feature-flag-guard

Feature/wallet api feature flag guard
…he-layer-stub

Feature/wallet cache layer stub
…-integration-tests

Feature/wallet api integration tests
Jambox11 and others added 28 commits August 29, 2026 14:42
…763-env-validation-cleanup

fix: env validation for Horizon retries, maintenance secret, and .env.example cleanup
…ion-cors-stellar-balance-sync

fix(config): validate CORS, Stellar network, and balance sync env vars
…port-signing

fix: include payment usage in daily limits and fail-closed export signing
…rage

Adds automated coverage for the internal cron endpoints
(/transactions/internal/*) confirming CronSecretGuard fails closed and
that a project API key alone is never sufficient to reach them — the
gap described in mux-labs#801 had no regression tests, so this was previously
unverified behavior.

Also hardens CronSecretGuard itself to match the fail-closed,
constant-time-comparison pattern already established by
InternalServiceGuard (mux-labs#690) in this codebase:
- Header comparison now uses crypto.timingSafeEqual instead of !==,
  removing a timing side-channel on the shared secret.
- Header/secret values are trimmed and array-valued headers are
  handled explicitly (only the first value is considered).
- Log lines include a request id and path for correlation, and never
  log the secret value itself.

No behavior change to the guard's pass/fail decisions for
already-well-formed requests; CRON_SECRET was already required at
startup in production via env.validation.ts and the guard already
failed closed when unset. This closes the actual observable gap:
missing test coverage, plus the latent timing side-channel.

Tests added:
- src/common/cron/cron-secret.guard.spec.ts: unit + HTTP-integration
  tests (unconfigured secret, missing header, wrong secret, correct
  secret, array-header handling, Authorization-header-is-not-enough).
- test/transactions-internal-cron-guard.e2e-spec.ts: e2e coverage of
  the real /v1/transactions/internal/* routes via the full AppModule,
  mirroring the existing backup-module-registered.e2e-spec.ts pattern.
- Add MetricsLabelGuardService to detect and sanitize high-cardinality labels
  (Stellar StrKey addresses, tx hashes, UUIDs, opaque tokens)
- Fail-fast in dev/test to catch bad instrumentation
- Sanitize to fixed placeholder in production with hard-cap on distinct combinations
- Update MetricsService to route all labels through the guard
- Fix label-set mismatch crash bug in prom-client
- Provide cardinality statistics for monitoring/debugging

Prevents unbounded Prometheus series explosion from wallet IDs or tx hashes
leaking into metric labels.
…ux-labs#803)

- Create comprehensive SECURITY.md with private disclosure process
- Define SLA for critical/high/medium/low severity vulnerabilities
- Specify in-scope security domains (wallet encryption, custody, cron auth, API keys, data integrity)
- Prevent public GitHub issues for custody/relayer vulnerabilities
- Provide private security contact: security@mux.com
- Define 90-day responsible disclosure timeline
- Establish fail-closed production requirements (WALLET_ENCRYPTION_KEY, CRON_SECRET)
- Document safe harbor for security researchers
- Add guardrails: never log secrets, no stack traces in errors, request ID tracing

Ensures Mux Backend can safely custody Stellar keys, relay sponsored txs,
and expose production /v1 API without vulnerability disclosure risks.
…n config (issue mux-labs#804)

- Add mainnet payment startup validation to TransactionEnvValidatorService
- In production, fail if FEATURE_MAINNET_PAYMENTS enabled but STELLAR_HORIZON_MAINNET_URL missing
- Validate STELLAR_HORIZON_MAINNET_URL is valid URL when mainnet payments enabled
- In dev/test, allow missing mainnet URL with warning
- Enforce fail-closed behavior: catch config gaps at boot, not at payment submission time

Prevents silent sponsorship failures and ensures mainnet fee-bump transactions
can be submitted to Horizon when the feature is enabled in production.
- mux-labs#789: Generate X-Request-ID when clients omit it
  - Fix middleware variable declaration (missing let keyword)
  - Generate UUID for all requests without X-Request-ID header
  - Enhance exception filter to include generated requestId in responses
  - Update error-handling tests to verify generation

- mux-labs#790: Export auth metrics on Prometheus scrape path
  - Verify auth metrics registered to prom-client global registry
  - Create comprehensive auth-metrics-export.e2e-spec.ts test suite
  - Ensure all metrics accessible on /v1/metrics endpoint

- mux-labs#791: Hash stored API keys; never persist plaintext secrets
  - Enhance validateApiKey with crypto.timingSafeEqual for timing-safe comparison
  - Verify SHA-256 hashing implementation
  - Create comprehensive api-key-hashing-security.e2e-spec.ts test suite
  - Confirm SafeLogger redaction of API keys

- mux-labs#792: Unify Clerk vs Better Auth provider paths
  - Create AuthProvider enum with CLERK and BETTER_AUTH values
  - Add provider validation to AuthPayloadValidator
  - Update README with supported providers documentation
  - Create comprehensive auth-provider-unification.e2e-spec.ts test suite

All implementations include fail-closed production safety, comprehensive testing, and documentation.
…responses

Implements mux-labs#693, mux-labs#694, mux-labs#695, mux-labs#696.

mux-labs#693 — WALLET_ENCRYPTION_KEY rotation & re-encryption job
- EncryptionService: optional predecessor key from WALLET_ENCRYPTION_KEY_PREVIOUS
  (never used to encrypt), plus hasPreviousKey() and reEncryptWithCurrentKey()
  which decrypts with the current key, falling back to the previous key, and
  re-wraps under the current key.
- WalletKeyReEncryptionService + internal endpoint
  POST /v1/internal/key-management/re-encrypt-wallet-keys
  (FeatureFlagGuard + InternalServiceGuard). Id-cursor paginated, idempotent,
  emits a structured summary log with the request id; refuses to run (400)
  when WALLET_ENCRYPTION_KEY_PREVIOUS is not set.

mux-labs#694 — reject default WALLET_ENCRYPTION_KEY in validateEnv()
- validateEnv() now fails fast on the documented placeholder keys (previously
  only EncryptionService rejected them).
- New optional WALLET_ENCRYPTION_KEY_PREVIOUS is validated: min length,
  not a placeholder, must differ from WALLET_ENCRYPTION_KEY.

mux-labs#695 — apply ResponseSanitizerInterceptor globally
- Registered via APP_INTERCEPTOR in AppModule so privateKey / encryptedSecret
  are redacted from every response, not just the orchestration controller.

mux-labs#696 — gate loadTestMode on GET /wallets
- WalletsService.findAll() returns 403 for loadTestMode=true when
  NODE_ENV=production; synthetic data stays available outside production.

Docs: README, .env.example and CHANGELOG-KEY-MANAGEMENT updated.
Tests: encryption rotation unit tests, WalletKeyReEncryptionService spec,
env-validation placeholder spec, loadTestMode gating spec, and a global
ResponseSanitizerInterceptor e2e spec.

chore: repair pnpm-lock.yaml (stale @nestjs/event-emitter snapshot + missing
@types/d3-* entries from an earlier bad merge) so pnpm install --frozen-lockfile
succeeds again.
Deleting a user only soft-deleted the User row, leaving custody wallets
ACTIVE (their encrypted Stellar keys could still sign/relay) and any
owned developers/projects/API keys dangling — there was no link from
Developer/Project back to User, so the ownership chain could not be
walked or cleaned up.

- Add nullable Developer.userId FK (onDelete: SetNull) with an
  email-match backfill migration; expose optional userId on
  POST /developers.
- UsersService.remove() now runs one atomic transaction: disables the
  user's wallets (DISABLED is terminal), soft-deletes owned developers
  and projects, REVOKEs their API keys so they stop authenticating,
  disables webhook endpoints, then soft-deletes the user. Any step
  failure rolls the whole deletion back (fail-closed; no NODE_ENV
  skip path).
- Logs carry request ids and only counts/IDs (never
  WALLET_ENCRYPTION_KEY, API keys, or seeds); emits users_deleted_total
  counter + deletion duration histogram.
- Tests: 6 new unit cases, an 8-case integration spec, and a 3-case
  e2e spec. Verified 9 of the new tests fail against the old
  implementation.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
Wallet nicknames are rendered in the dashboard and consumed by the public
/v1 API, so unsanitized input is a stored-XSS vector and duplicate labels
make it impossible to distinguish custodied wallets. updateNickname now:

- Sanitizes the label (HTML tag-like sequences, javascript: schemes,
  inline on* handlers, control chars) before persisting or returning it.
- Enforces case-insensitive uniqueness across the owner's non-archived
  wallets, returning 409 Conflict on a collision; clearing never triggers
  the check, and a value that sanitizes to empty is treated as a clear.
- Emits an update_nickname metric and structured logs carrying the
  x-request-id and userId (never secret material).

Adds unit coverage in wallet-nickname.spec.ts and a controller e2e in
test/wallet-nickname.e2e-spec.ts, and documents the behavior in the
README and OpenAPI DTO.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…ming-safe-and-tests

fix(mux-labs#801): timing-safe CronSecretGuard comparison + test coverage
…etric-cardinality

fix: bound prometheus metric cardinality for wallet/transaction ids
…y-md-private-disclosure

feat: harden SECURITY.md for private vulnerability disclosure (issue …
…-mainnet-payment-without-horizon-feesource

fix: fail production boot when mainnet payment enabled without Horizo…
…ility-tasks-789-792

feat: Implement security & reliability tasks mux-labs#789-mux-labs#792
…696-wallet-encryption-key-rotation-and-response-hardening

feat(key-management): WALLET_ENCRYPTION_KEY rotation + wallet response hardening
fix(users): clean up owned projects/developers/wallets on user deletion
…-nickname-sanitize-uniqueness

feat(wallets): sanitize wallet nickname labels & enforce per-owner uniqueness
RecoveryService injects PrismaService directly but RecoveryModule
relied on PrismaModule's @global() decorator to resolve it. This
fragile coupling breaks test isolation when the global module is
not loaded.

- Add PrismaModule to RecoveryModule imports array
- Add module spec verifying compilation, provider resolution, and
  that PrismaModule appears in the imports metadata

Closes mux-labs#772

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Jambox11
Jambox11 merged commit 2bf52c0 into mux-labs:main Sep 4, 2026
Jambox11 pushed a commit that referenced this pull request Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.